﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright company="DHGMS Solutions" file="Outward.cs">
//   2004-2012 DHGMS Solutions. Some Rights Reserved. Licensed under GNU General Public License version 2 (GPLv2)
// </copyright>
// <summary>
//   Represents the outward section of a UK Postcode
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace Dhgms.DataManager.Model.Info.Raw.GBPostcode
{
    using System;
    using System.Linq;

    using Dhgms.DataManager.Model.Excptn;
    using Dhgms.DataManager.Model.Helper;

    /// <summary>
    /// Represents the outward section of a UK Postcode
    /// </summary>
    public class Outward
    {
        #region Constants and Fields

        private static readonly string[] BadStartingLetters = { "Q", "V", "X" };

        private static readonly string[] BadSecondLetters = { "I", "J", "Z" };

        /// <summary>
        /// The _area.
        /// </summary>
        private string _area;

        /// <summary>
        /// The _district.
        /// </summary>
        private string _district;

        #endregion

        #region Constructors and Destructors

        /// <summary>
        /// Initializes a new instance of the <see cref="Outward"/> class. 
        /// The outward.
        /// </summary>
        /// <param name="value">
        /// </param>
        public Outward(string value)
        {
            if (string.IsNullOrWhiteSpace(value))
            {
                throw new ArgumentNullException("value");
            }

            if (value.Length < 1 || value.Length > 4)
            {
                throw new ArgumentException("outward must be between 1 and 4 in length");
            }

            switch (value.Length)
            {
                case 1:
                    this.Area = value;
                    break;
                case 2:
                    this.Area = value.Left(1);
                    this.District = value.Right(1);
                    break;
                case 3:
                    if (char.IsDigit(value[2]))
                    {
                        // 2nd part is a number
                        this.Area = value.Left(1);
                        this.District = value.Right(2);
                    }
                    else if (char.IsLetter(value[2]))
                    {
                        this.Area = value.Left(2);
                        this.District = value.Right(1);
                    }
                    else
                    {
                        throw new PostcodeBadFormatException();
                    }

                    break;
                case 4:
                    this.Area = value.Left(2);
                    this.District = value.Right(2);
                    break;
                default:
                    throw new ArgumentNullException("value");
            }
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Outward"/> class. 
        /// The outward.
        /// </summary>
        /// <param name="area">
        /// </param>
        /// <param name="district">
        /// </param>
        public Outward(string area, string district)
        {
            if (string.IsNullOrWhiteSpace(area))
            {
                throw new ArgumentNullException("area");
            }

            if (string.IsNullOrWhiteSpace(district))
            {
                throw new ArgumentNullException("district");
            }

            if (!area.Length.Between(1, 2))
            {
                throw new ArgumentException("area must be 1 or 2 characters in length.", "area");
            }

            if (!district.Length.Between(1, 2))
            {
                throw new ArgumentException("district must be 1 or 2 characters in length.", "district");
            }

            this.Area = area;
            this.District = district;
        }

        #endregion

        #region Public Properties

        /// <summary>
        /// Represents the area of a postcode
        /// </summary>
        public string Area
        {
            get
            {
                return this._area;
            }

            set
            {
                if (string.IsNullOrWhiteSpace(value))
                {
                    throw new ArgumentNullException("value");
                }

                if (!value.Length.Between(1, 2))
                {
                    throw new ArgumentException("Area must be between 1 and 2 characters in length.", "value");
                }

                var letter = value.Substring(0, 1);
                if (!char.IsLetter(value[0]) || BadStartingLetters.Any(x => string.Equals(x, letter, StringComparison.OrdinalIgnoreCase)))
                {
                    throw new ArgumentException("first letter of area code is invalid character", "value");
                }

                if (value.Length == 2)
                {
                    letter = value.Substring(1, 1);
                    if (!char.IsLetterOrDigit(value[1]) || BadSecondLetters.Any(x => string.Equals(x, letter, StringComparison.OrdinalIgnoreCase)))
                    {
                        throw new ArgumentException("second letter of area code is invalid character", "value");
                    }
                }

                this._area = value;
            }
        }

        /// <summary>
        /// Represents the district of a postcode
        /// </summary>
        public string District
        {
            get
            {
                return this._district;
            }

            set
            {
                if (string.IsNullOrWhiteSpace(value))
                {
                    throw new ArgumentNullException("value");
                }

                var chars = value.ToCharArray();

                if (chars.Length < 1 || chars.Length > 2)
                {
                    throw new ArgumentException("District must be between 1 and 2 characters in length.", "value");
                }

                if (!char.IsDigit(chars[0]) || !char.IsLetterOrDigit(chars[0]))
                {
                    throw new ArgumentException("area not correct format", "value");
                }

                this._district = value;
            }
        }

        /// <summary>
        /// Whether the section of the postcode is valid
        /// </summary>
        public bool IsValid
        {
            get
            {
                return !string.IsNullOrEmpty(this.Area) && !string.IsNullOrEmpty(this.District);
            }
        }

        #endregion

        #region Public Methods and Operators

        /// <summary>
        /// formats the postcode section as a string
        /// </summary>
        /// <returns>
        /// formatted outward postcode section
        /// </returns>
        public override string ToString()
        {
            return this.Area + this.District;
        }

        #endregion
    }
}